﻿// Author: Hristo Hristov
// Date: 08.09.11
// Revision 2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

using Nuclex.Graphics;
using Nuclex.Fonts;

namespace NOVA.Scenery
{
    /// <summary>
    /// Creates a 3D text object for the scene.
    /// </summary>
    public class Text3D
    {
        /// <summary>
        /// Defines the different types of text formats.
        /// </summary>
        public enum Type
        {
            Fill = 0,
            Outline,
            Extrude
        }

        #region Members

        Vector3 m_position;
        Quaternion m_rotation;
        Vector3 m_scale;

        Color m_color;

        SpriteFont m_spriteFont;
        VectorFont m_spriteVector;
        Text m_text;
        TextBatch m_textBatch;

        TimeSpan m_lifetime;
        long m_lLifetimeBase;
        bool m_bHasLifetime;

        BasicEffect effect;

        Vector3 m_linearVelocity;
        float m_fOneSecond = (float)TimeSpan.FromSeconds(1).Ticks;

        bool m_bExpired;
        bool m_bNoRemoveWhenExpired;

        #endregion

        #region Properties
        public Vector3 Position
        {
            get
            {
                return m_position;
            }
            set
            {
                m_position = value;
            }
        }

        public Quaternion Orientation
        {
            get
            {
                return m_rotation;
            }
            set
            {
                m_rotation = value;
            }
        }

        public Vector3 Scale
        {
            get
            {
                return m_scale;
            }
            set
            {
                m_scale = value;
            }
        }

        public float Width
        {
            get { return m_text.Width; }
        }

        public float Height
        {
            get { return m_text.Height; }
        }

        public Color Color
        {
            get { return m_color; }
            set { m_color = value; }
        }

        public TimeSpan Lifetime
        {
            get { return m_lifetime; }
            set 
            { 
                m_lifetime = value;
                m_lLifetimeBase = m_lifetime.Ticks;
                m_bHasLifetime = true;
            }
        }

        public Vector3 LinearVelocity
        {
            get { return m_linearVelocity; }
            set { m_linearVelocity = value; }
        }

        public bool Expired
        {
            get { return m_bExpired; }
        }

        public bool NoRemoveWhenExpired
        {
            get { return m_bNoRemoveWhenExpired; }
            set { m_bNoRemoveWhenExpired = value; }
        }
        #endregion

        #region Constructor

        /// <summary>
        /// Creates a 3D text object for the scene.
        /// </summary>
        public Text3D(Vector3 position, Type type, string text, Color color, string fontAsset)
        {
            m_position = position;
            m_rotation = Quaternion.Identity;
            m_scale = Vector3.One * 3;

            m_color = color;

            m_textBatch = new TextBatch(Core.Device);

            if (fontAsset == null || fontAsset == "")
                m_spriteVector = Core.EmbeddedContent.Load<VectorFont>("Arial_24_Vector");
            else
            {
                try
                {
                    m_spriteVector = Core.Content.Load<VectorFont>(fontAsset);
                }
                finally
                {
                    m_spriteVector = Core.EmbeddedContent.Load<VectorFont>("Arial_24_Vector");
                }
            }

            // Create the 3D text depending on which type
            switch (type)
            {
                case Type.Fill: m_text = m_spriteVector.Fill(text); break;
                case Type.Outline: m_text = m_spriteVector.Outline(text); break;
                case Type.Extrude: m_text = m_spriteVector.Extrude(text); break;
            }

            m_lifetime = TimeSpan.Zero;
            m_bHasLifetime = false;

            effect = new BasicEffect(Core.Device);

            m_linearVelocity = Vector3.Zero;

            m_bExpired = false;
        }
        #endregion

        #region Public Methods

        /// <summary>
        /// Renders the 3D text object.
        /// </summary>
        public void Draw(GameTime gameTime)
        {
            // Apply the velocity
            if (m_linearVelocity != Vector3.Zero)
                m_position += m_linearVelocity * ((float)gameTime.ElapsedGameTime.Ticks / m_fOneSecond);

            if (m_bHasLifetime)
            {
                // Reduce the lifetime by the elapsed game time
                m_lifetime = m_lifetime.Subtract(gameTime.ElapsedGameTime);
                
                // If the lifetime isn't over, render the text with an 
                // alpha blend factor depending on the remaining lifetime
                int iRemainLifetime = m_lifetime.CompareTo(TimeSpan.Zero);
                if (iRemainLifetime > 0)
                    Render((float)m_lifetime.Ticks / (float)m_lLifetimeBase);
                else
                {
                    // Used internally for removing this text, when it expires
                    m_bExpired = true;
                }
            }
            else
                Render(1f); // Fully opaque            
        }
        #endregion

        #region Private Methods

        /// <summary>
        /// Renders the 3D text.
        /// </summary>
        private void Render(float alpha)
        {
            // Calculate the world matrix
            Matrix textTransform = Matrix.CreateTranslation(-m_text.Width / 2.0f, 0.0f, 0.0f) * // Center the text
                                               Matrix.CreateScale(m_scale) *
                                               Matrix.CreateFromQuaternion(m_rotation) *
                                               Matrix.CreateTranslation(m_position);
            
            // Define the basic effect
            effect.World = textTransform;
            effect.View = Core.ViewMatrix;
            effect.Projection = Core.ProjectionMatrix;
            effect.DiffuseColor = m_color.ToVector3();
            effect.SpecularColor = Color.White.ToVector3();
            effect.PreferPerPixelLighting = false;
            effect.Alpha = alpha;

            // Render the 3D text
            m_textBatch.Begin();

            m_textBatch.DrawText(m_text, effect);

            m_textBatch.End();
        }
        #endregion
    }
}
